package com.gitee.yongzhuzl.commonutil.util.qr;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import org.springframework.util.StringUtils;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;

/**
 * description: 二维码相关工具类
 * @author 朱正磊
 * @date 2023-11-30 10:41:30
 */
public class QRCodeUtil {

    /**
     * 二维码尺寸，宽度和高度均是250
     */
    private static final int QRCODE_SIZE = 250;

    /**
     * 二维码图片类型
     */
    private static final String FORMAT_TYPE = "png";

    /**
     * description: 获取二维码图片
     * @author 朱正磊
     * @date 2023-11-30 10:45:55
     * @param content 二维码内容
     * @param logoPath logo图片路径，为空则不显示
     * @param bottomText 底部文字，为空则不显示
     * @return 返回字节数组
     * @throws Exception 异常信息
     */
    public static byte[] getQRCodeByte(String content, String logoPath, String bottomText) throws Exception {
        BufferedImage bufferedImage = getQRCodeImage(content, logoPath, bottomText);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ImageIO.write(bufferedImage, FORMAT_TYPE, outputStream);
        return outputStream.toByteArray();
    }

    /**
     * description: 获取二维码图片
     * @author 朱正磊
     * @date 2023-11-30 14:26:53
     * @param content 二维码内容
     * @return 返回二维码图片
     * @throws Exception 异常信息
     */
    public static BufferedImage getQRCodeImage(String content) throws Exception {
        return getQRCodeImage(content, null, null);
    }

    /**
     * description: 获取二维码图片
     * @author 朱正磊
     * @date 2023-11-30 14:26:53
     * @param content 二维码内容
     * @param bottomText 底部文字，为空则不显示
     * @return 返回二维码图片
     * @throws Exception 异常信息
     */
    public static BufferedImage getQRCodeImage(String content, String bottomText) throws Exception {
        return getQRCodeImage(content, null, bottomText);
    }

    /**
     * description: 获取二维码图片
     * @author 朱正磊
     * @date 2023-11-30 10:52:29
     * @param content 二维码内容
     * @param logoPath logo图片路径，为空则不显示
     * @param bottomText 底部文字，为空则不显示
     * @return 返回二维码图片
     * @throws Exception 异常信息
     */
    public static BufferedImage getQRCodeImage(String content, String logoPath, String bottomText) throws Exception {
        if (content == null) {
            throw new RuntimeException("未包含任何信息");
        }
        HashMap<EncodeHintType, Object> hints = new HashMap<>();
        // 定义内容字符集的编码
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
        // 定义纠错等级
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);
        hints.put(EncodeHintType.MARGIN, 1);
        QRCodeWriter qrCodeWriter = new QRCodeWriter();
        BitMatrix bitMatrix = qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, hints);

        int width = bitMatrix.getWidth();
        int height = bitMatrix.getHeight();
        int tempHeight = height;
        if (StringUtils.hasText(bottomText)) {
            tempHeight = tempHeight + 12;
        }
        BufferedImage image = new BufferedImage(width, tempHeight, BufferedImage.TYPE_INT_RGB);
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
            }
        }
        // 判断是否添加logo
        if (StringUtils.hasText(logoPath)) {
            insertLogoImage(image, logoPath);
        }
        // 判断是否添加底部文字
        if (StringUtils.hasText(bottomText)) {
            addFontImage(image, bottomText);
        }
        return image;
    }

    /**
     * description: 插入logo图片
     * @author 朱正磊
     * @date 2023-11-30 10:53:03
     * @param source 二维码图片
     * @param logoPath logo图片路径
     * @throws Exception 异常信息
     */
    private static void insertLogoImage(BufferedImage source, String logoPath) throws Exception {
        InputStream inputStream = Files.newInputStream(Paths.get(logoPath));
        if (inputStream.available() == 0) {
            return;
        }
        Image src = ImageIO.read(inputStream);
        int width = 30;
        int height = 30;

        Image image = src.getScaledInstance(width, height, Image.SCALE_SMOOTH);
        BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics g = tag.getGraphics();
        // 绘制缩小后的图
        g.drawImage(image, 0, 0, null);
        g.dispose();
        src = image;

        // 插入LOGO
        Graphics2D graph = source.createGraphics();
        int x = (QRCODE_SIZE - width) / 2;
        int y = (QRCODE_SIZE - height) / 2;
        graph.drawImage(src, x, y, width, height, null);
        Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);
        graph.setStroke(new BasicStroke(3f));
        graph.draw(shape);
        graph.dispose();
    }

    /**
     * description: 添加底部文字
     * @author 朱正磊
     * @date 2023-11-30 10:53:44
     * @param source 二维码图片
     * @param declareText 底部文字
     */
    private static void addFontImage(BufferedImage source, String declareText) {
        // 生成image
        int defineWidth = QRCODE_SIZE;
        int defineHeight = 25;
        BufferedImage textImage = new BufferedImage(defineWidth, defineHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2 = (Graphics2D) textImage.getGraphics();
        // 开启文字抗锯齿
        g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        g2.setBackground(Color.WHITE);
        g2.clearRect(0, 0, defineWidth, defineHeight);
        g2.setPaint(Color.BLACK);
        FontRenderContext context = g2.getFontRenderContext();
        // 部署linux需要注意 linux无此字体会显示方块
        Font font = new Font("黑体", Font.BOLD, 20);
        g2.setFont(font);
        LineMetrics lineMetrics = font.getLineMetrics(declareText, context);
        FontMetrics fontMetrics = g2.getFontMetrics();
        float offset = (defineWidth - fontMetrics.stringWidth(declareText)) >> 1;
        float y = (defineHeight + lineMetrics.getAscent() - lineMetrics.getDescent() - lineMetrics.getLeading()) / 2;
        g2.drawString(declareText, (int) offset, (int) y - 3);

        Graphics2D graph = source.createGraphics();
        // 开启文字抗锯齿
        graph.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        // 添加image
        int width = textImage.getWidth();
        int height = textImage.getHeight();

        graph.drawImage(textImage, 0, QRCODE_SIZE - 8, width, height, Color.WHITE, null);
        graph.dispose();
    }

}
